#ifndef __BYTECODE_H__
#define __BYTECODE_H__

#include <stdint.h>
#include "rstring.h"
#include "redobject.h"

/* instruction set */

#define OP_OBJECT		0x8000
#define OP_OFFSET		0x4000

typedef enum _opcode_t
{
	LOAD_CONST		= 0x0000 | OP_OBJECT,		// LOAD_CONST		<const>		push <const>
	LOAD_OBJECT		= 0x0001 | OP_OBJECT,		// LOAD_OBJECT		<name>		push <name>
	STOR_OBJECT		= 0x0002 | OP_OBJECT,		// STOR_OBJECT		<name>		pop -> <name>
	DEL_OBJECT		= 0x0003 | OP_OBJECT,		// DEL_OBJECT		<name>		delete <name>

	GET_ATTR		= 0x0004 | OP_OBJECT,		// GET_ATTR			<name>		<stack_top> = <stack_top>.<name>
	SET_ATTR		= 0x0005 | OP_OBJECT,		// SET_ATTR			<name>		<stack_top + 1>.<name> = <stack_top>
	DEL_ATTR		= 0x0006 | OP_OBJECT,		// DEL_ATTR			<name>		delete <stack_top>.name

	GET_ITEM		= 0x0007,					// GET_ITEM						<stack_top> = <stack_top + 1>[<stack_top>]
	SET_ITEM		= 0x0008,					// SET_ITEM						<stack_top + 2>[<stack_top + 1>] = <stack_top>
	DEL_ITEM		= 0x0009,					// DEL_ITEM						delete <stack_top + 1>[<stack_top>]

	INVOKE			= 0x000A | OP_OFFSET,		// INVOKE			<nargs>		<stack_top> = <stack_top>(<stack_top + 1> .. <stack_top + <nargs>>)
	POP_RETURN		= 0x000B,					// RETURN						return <stack_top>

	ADD				= 0x000C,					// ADD							<stack_top> = <stack_top + 1> + <stack_top>
	SUB				= 0x000D,					// ...
	MUL				= 0x000E,
	DIV				= 0x000F,
	MOD				= 0x0010,
	POW				= 0x0011,
	BIT_OR			= 0x0012,
	BIT_AND			= 0x0013,
	BIT_XOR			= 0x0014,
	BIT_NOT			= 0x0015,					// BIT_NOT						<stack_top> = ~<stack_top>
	LSHIFT			= 0x0016,
	RSHIFT			= 0x0017,

	AUG_ADD			= 0x0018,					// AUG_ADD						<stack_top + 1> += <stack_top>
	AUG_SUB			= 0x0019,					// ...
	AUG_MUL			= 0x001A,
	AUG_DIV			= 0x001B,
	AUG_MOD			= 0x001C,
	AUG_POW			= 0x001D,
	AUG_BIT_OR		= 0x001E,
	AUG_BIT_AND		= 0x001F,
	AUG_BIT_XOR		= 0x0020,
	AUG_LSHIFT		= 0x0021,
	AUG_RSHIFT		= 0x0022,

	BOOL_OR			= 0x0023,					// BOOL_OR						<stack_top> = <stack_top + 1> or <stack_top>
	BOOL_AND		= 0x0024,					// ...
	BOOL_XOR		= 0x0025,
	BOOL_NOT		= 0x0026,					// BOOL_NOT						<stack_top> = not <stack_top>

	EQ				= 0x0027,					// EQ							<stack_top> = <stack_top + 1> == <stack_top>
	LE				= 0x0028,					// ...
	GE				= 0x0029,
	NEQ				= 0x002A,
	LEQ				= 0x002B,
	GEQ				= 0x002C,

	POS				= 0x002D,					// POS							<stack_top> = +<stack_top>
	NEG				= 0x002E,					// NEG							<stack_top> = -<stack_top>

	DUP				= 0x002F,					// DUP							Duplicate <stack_top>
	DUP2			= 0x0030,					// DUP2							Duplicate <stack_top> and <stack_top - 1>
	DROP			= 0x0031,					// DROP							Drop <stack_top>

	LOAD_ARG		= 0x0032 | OP_OBJECT,		// LOAD_ARG			<arg>		Store <stack_top> as argument
	MAKE_FUNCTION	= 0x0033 | OP_OFFSET,		// MAKE_FUNCTION	<nargs>		Store bytecodes into new function

	BR				= 0x0034 | OP_OFFSET,		// BR				<pc>		Branch to <pc>
	BRTRUE			= 0x0035 | OP_OFFSET,		// BRTRUE			<pc>		Branch to <pc> if <stack_top> represents True
	BRFALSE			= 0x0036 | OP_OFFSET,		// BRFALSE			<pc>		Branch to <pc> if <stack_top> represents False

	RANGE			= 0x0037,					// RANGE						<stack_top> = <stack_top + 2> in {<stack_top + 1> .. <stack_top>}

	PUSH_BLOCK		= 0x0038 | OP_OBJECT,		// PUSH_BLOCK		<block>		Load exception rescure block
	POP_BLOCK		= 0x0039,					// POP_BLOCK					Restore stack and destroy rescure block

	EXPAND			= 0x0040,					// EXPAND						Expand <stack_top>
	START_ARGS		= 0x0041,					// START_ARGS					Start filling variable args
	INVOKE_VARG		= 0x0042,					// INVOKE_VARG					Invoke as variable argument
} opcode_t;

typedef struct _bytecode_t
{
	int				 ref;
	opcode_t		 opcode;
	int				 lineno;
	int				 position;

	union
	{
		int			 at_offset;
		RedObject	*at_object;
	};
} bytecode_t;

RED_EXPORT void bytecode_ref(bytecode_t *self);
RED_EXPORT void bytecode_unref(bytecode_t *self);
RED_EXPORT void bytecode_clear(bytecode_t *self);
RED_EXPORT void bytecode_release(bytecode_t **self);
RED_EXPORT bytecode_t *bytecode_create(opcode_t opcode, int lineno, int position);

#define stringify(op)	case op: return #op;

static inline const char *bytecode_opcode_name(opcode_t opcode)
{
	switch (opcode)
	{
		stringify(LOAD_CONST)
		stringify(LOAD_OBJECT)
		stringify(STOR_OBJECT)
		stringify(DEL_OBJECT)

		stringify(GET_ATTR)
		stringify(SET_ATTR)
		stringify(DEL_ATTR)

		stringify(SET_ITEM)
		stringify(GET_ITEM)
		stringify(DEL_ITEM)

		stringify(INVOKE)
		stringify(POP_RETURN)

		stringify(ADD)
		stringify(SUB)
		stringify(MUL)
		stringify(DIV)
		stringify(MOD)
		stringify(POW)
		stringify(BIT_OR)
		stringify(BIT_AND)
		stringify(BIT_XOR)
		stringify(BIT_NOT)
		stringify(LSHIFT)
		stringify(RSHIFT)

		stringify(AUG_ADD)
		stringify(AUG_SUB)
		stringify(AUG_MUL)
		stringify(AUG_DIV)
		stringify(AUG_MOD)
		stringify(AUG_POW)
		stringify(AUG_BIT_OR)
		stringify(AUG_BIT_AND)
		stringify(AUG_BIT_XOR)
		stringify(AUG_LSHIFT)
		stringify(AUG_RSHIFT)

		stringify(BOOL_OR)
		stringify(BOOL_AND)
		stringify(BOOL_XOR)
		stringify(BOOL_NOT)

		stringify(EQ)
		stringify(LE)
		stringify(GE)
		stringify(NEQ)
		stringify(LEQ)
		stringify(GEQ)

		stringify(POS)
		stringify(NEG)

		stringify(DUP)
		stringify(DUP2)
		stringify(DROP)

		stringify(LOAD_ARG)
		stringify(MAKE_FUNCTION)

		stringify(BR)
		stringify(BRTRUE)
		stringify(BRFALSE)

		stringify(RANGE)

		stringify(PUSH_BLOCK)
		stringify(POP_BLOCK)
			
		stringify(EXPAND)
		stringify(START_ARGS)
		stringify(INVOKE_VARG)

		default:
			return "<unknown>";
	}
}

#undef stringify
#endif /* __BYTECODE_H__ */
